package com.xuxueli.applyModules.service;
import com.aspose.cells.*;
import com.itextpdf.text.Document;
import com.itextpdf.text.pdf.PdfCopy;
import com.itextpdf.text.pdf.PdfReader;
import com.xuxueli.applyModules.entity.*;
import com.xuxueli.applyModules.exception.ErrorException;
import com.xuxueli.applyModules.dto.DcBillHeadDto;
import com.xuxueli.applyModules.dto.DcBillListDto;
import com.xuxueli.applyModules.dto.DcEntryListDto;
import com.xuxueli.applyModules.dto.EmsDcrHeadPrintDto;
import com.xuxueli.applyModules.entity.*;
import com.xuxueli.applyModules.mapper.HxPartitionMapper;
import com.xuxueli.applyModules.utils.BarcodeUtil;
import com.xuxueli.applyModules.utils.ExportUtil;
import com.xuxueli.applyModules.utils.ResultObject;
import com.xuxueli.applyModules.utils.StringFilterUtils;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.javatuples.Pair;
import org.javatuples.Triplet;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.core.io.ClassPathResource;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.math.BigDecimal;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.stream.Collectors;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;

@Service
@Slf4j
public class TestService {

    @Resource
    private HxPartitionMapper partitionMapper;
    @Value("${DEPLOY_TARGET}")
    private String defaultUrl;
    @Value("${dc.export.temp}")
    public String tempPath;

    /**
     * 获取列表
     * @param peoplePara
     * @return
     */
    public List<People> getList(PeoplePara peoplePara){
        Map<String,Object> map = new HashMap<>();
        List<People> peopleList = new ArrayList<>();
        for (People people : peopleList) {
            people.setDesQtyStr(people.getDesQty() == null ? "0": people.getDesQty().stripTrailingZeros().toPlainString());
        }
        return peopleList;
    }


    /**
     * 获取异常，内部抛出的异常try catch可以接受到手动抛出的异常，最底层也是Exception
     * @param people
     * @return
     */
    public ResultObject getException(Domain people) {
        ResultObject resultObject = ResultObject.createInstance(true,"新增成功");
        try {
            //getValue(people);
        }catch (ErrorException er){
            String message = er.getMessage();
            if(StringUtils.isBlank(message)){
                message = er.toString();
            }
            resultObject.setCode(er.getCode());
            resultObject.setSuccess(false);
            resultObject.setMessage(message);
        }catch (Exception er){
            String message = er.getMessage();
            if(StringUtils.isBlank(message)){
                message = er.toString();
            }
            resultObject.setCode(400);
            resultObject.setSuccess(false);
            resultObject.setMessage(message);
        }
        return resultObject;
    }


    /**
     * 是否能初始化
     */
    public ResultObject getDefalutValue(String value){
        ResultObject resultObject =  ResultObject.createInstance();
        if(StringUtils.isBlank(defaultUrl)){
            resultObject.setCode(400);
            resultObject.setSuccess(false);
            resultObject.setMessage("初始化失败");
        }else{
            resultObject.setData(defaultUrl);
        }
        return resultObject;
    }

    /**
     * 获取需打印数据
     * @return
     */
    public List<EmsDcrHeadPrintDto> printDcrHead(String emsNo, Integer dcrTimes) {
        SimpleDateFormat dateFormat = new SimpleDateFormat("yyyyMMdd");
        Map<String, Object> param = new HashMap<>(3);
        param.put("tradeCode", "666");
        param.put("emsNo", emsNo);
        param.put("dcrTimes",dcrTimes);
        List<EmsDcrHeadPrintDto> emsDcrHeadDtoList = new ArrayList<>();
        EmsDcrHeadPrintDto emsDcrHeadPrintDto = new EmsDcrHeadPrintDto();
        emsDcrHeadPrintDto.setTradeCode("666");
        emsDcrHeadPrintDto.setTradeName("你真棒有限公司"+":");
        emsDcrHeadPrintDto.setEmsNo("E668899");
        emsDcrHeadPrintDto.setDcrTimesStr(dcrTimes+"次");
        emsDcrHeadPrintDto.setDcrStartTime("("+dateFormat.format(new Date())+("-"+dateFormat.format(new Date()))+")");
        emsDcrHeadPrintDto.setMasterCustoms("我是海关");
        emsDcrHeadPrintDto.setPrintTime(dateFormat.format(new Date()));
        emsDcrHeadPrintDto.setSumBillCnt(66+"份。");
        emsDcrHeadDtoList.add(emsDcrHeadPrintDto);
        return emsDcrHeadDtoList;
    }

    /***
     * @param head
     * @param fileName
     * @param tname
     * @param <H>
     */
    public <H> String exportDcrHeadExcel(List<H> head, String fileName, String tname,String... footer) {
        // 验证License
        if (!getLicense()) {
            return "";
        }
        Properties props=System.getProperties(); //系统属性或者直接用System.getProperty("propertyName");
        try {
            //临时文件路径
            String tempfile = props.getProperty("user.dir") +"/temp/"+ fileName;
            //模板路径
            ClassPathResource resource = new ClassPathResource(tname);
            String fp = resource.getPath();
            //String fp = props.getProperty("user.dir")+"/template/" + tname;
            if (head.size() > 0) {
                Workbook wb = new Workbook(fp);
                WorkbookDesigner designer = new WorkbookDesigner();
                if(footer.length > 0) {
                    //设置页脚的参数
                    setFooter(wb, footer);
                }
                designer.setWorkbook(wb);
                designer.setDataSource("DcrHead", head);
                designer.process();
                WorksheetCollection wc = wb.getWorksheets();
                AutoFitterOptions options = new AutoFitterOptions();
                options.setAutoFitMergedCells(true);
                if ("iTemplate.xlsx".equalsIgnoreCase(tname) || "eBillTemplate.xlsx".equalsIgnoreCase(tname)) {
                    //插入条形码图片
                    for (int i = 0; i < head.size(); i++) {
                        EmsDcrHeadPrintDto printDto = (EmsDcrHeadPrintDto)head.get(i);
                        if (printDto.getTradeCode()!=null){
                            BarcodeUtil.createBarcode(printDto.getTradeCode(),tempfile+printDto.getTradeCode()+".png");
                            wc.get(i).getPictures().add(1, 10,tempfile+printDto.getTradeCode()+".png");
                        }
                    }
                    wc.get(1).autoFitRows(3, 3+head.size(), options);
                    wc.get(2).autoFitRows(2, 2+head.size(), options);
                }

                if ("iDraft.xls".equalsIgnoreCase(tname)) {
                    wc.get(0).autoFitRows(20, 20+head.size(), options);
                }
                wb.save(tempfile);
                wb.dispose();
                return tempfile;
            }

        } catch (Exception ex) {
            log.error(ex.getMessage(), ex);
        }
        return "";
    }

    /**
     * 查询模板中 页码/页数: 字段的值
     * 第一页中表头+3条表体为一页，大于3条表体为两页
     * 第二页中9条表体+其他数据为一整页，整十条表体也为一整页
     * @param pairVal
     * @return
     */
    private String selectPageCounts(Pair<List<PeoplePara>, List<PeoplePara>> pairVal) {
        int pc = 1;
        //如果list.size>3,那么减4之后对10取余
        if(pairVal.getValue1().size() > 3){
            int size = pairVal.getValue1().size()-4;
            //去除3条后是否大于等于10条
            int pageCount;
            if(size >= 10){
                pageCount = size/10;
                pc = pc + pageCount + 1;
            }else{
                //如果小于十条
                pc = pc + 1;
            }
        }
        return "1/"+pc;
    }

    /**
     * 如果是人工归并，判断表体中的序号是否连续
     * @param dcBillHead
     * @param dcBillList
     * @return
     */
    private void checkEntryGNoSequence(DcBillHeadDto dcBillHead, List<DcBillListTmpParam> dcBillList) {
        if ("1".equals(dcBillHead.getMergeType())) {
            List<BigDecimal> entryGNos = dcBillList.stream().map(el -> el.getEntryGNo()).distinct().collect(Collectors.toList());
            Collections.sort(entryGNos);
            for (int i = 0; i < entryGNos.size() - 1; i++) {
                int nextEntryGNo = entryGNos.get(i + 1).intValue();
                int currentEntryGNo = entryGNos.get(i).intValue();
                int diff = nextEntryGNo - currentEntryGNo;
                if (diff != 1) {
                    dcBillList.stream().filter(e -> nextEntryGNo <= e.getEntryGNo().intValue()).collect(Collectors.toList()).forEach(el -> {
                        el.setTempFlag(new BigDecimal(1));
                        el.setTempRemark(String.format("归并序号不连续，可能为序号为%d的数据错误或不存在，请检查！", nextEntryGNo - 1));
                    });
                }
            }
        }
    }

    /***
     * 合并pdf文件
     * @param pdfFilenames
     * @param filename
     * @return
     * @throws Exception
     */
    public String combinPdf(List<String> pdfFilenames, String filename) throws Exception {
        String targetFilename = "D:\\tmp\\" + filename;
        PdfReader reader = null;
        Document doc = new Document();
        PdfCopy pdfCopy = new PdfCopy(doc, new FileOutputStream(targetFilename));
        int pageCount = 0;
        doc.open();
        if (pdfFilenames.size()>0) {
            for (int i = 0; i < pdfFilenames.size(); ++i) {
                reader = new PdfReader(pdfFilenames.get(i));
                pageCount = reader.getNumberOfPages();
                for (int j = 1; j <= pageCount; ++j) {
                    pdfCopy.addPage(pdfCopy.getImportedPage(reader, j));
                }
            }
        }
        doc.close();
        return targetFilename;
    }

    public String combinExcel(String file1, String file2, String sheetName) throws Exception {
        try {
            String exportFileName = UUID.randomUUID().toString() + ".xlsx";
            Workbook wb1 = new Workbook("D:\\tmp\\" + file1);
            Workbook wb2 = new Workbook("D:\\tmp\\" + file2);
            wb2.getWorksheets().get(0).setName(sheetName);
            wb1.combine(wb2);
            wb1.save("D:\\tmp\\" + exportFileName);
            return exportFileName;
        } catch (Exception ex) {
            log.error(ex.getMessage(), ex);
            return file1;
        }
    }

    public String toZip(List<String> srcFiles, Map<String, String> fileNameMap) throws RuntimeException {
        long start = System.currentTimeMillis();
        String fileName = "D:\\tmp\\" + UUID.randomUUID().toString() + ".zip";
        ZipOutputStream zos = null;
        try {
            FileOutputStream out = new FileOutputStream(fileName);
            zos = new ZipOutputStream(out);
            for (String srcFile : srcFiles) {
                byte[] buf = new byte[4096];
                zos.putNextEntry(new ZipEntry(fileNameMap.get(srcFile)+".pdf"));
                int len;
                FileInputStream in = new FileInputStream(srcFile);
                while ((len = in.read(buf)) != -1) {
                    zos.write(buf, 0, len);
                }
                in.close();
            }
            zos.setComment("导出文件");
            zos.flush();
            zos.closeEntry();
            long end = System.currentTimeMillis();
            log.info("导出文件打包完成，耗时：" + (end - start) + " ms");
        } catch (Exception e) {
            throw new ErrorException(400, "打包文件时出错:"+e.toString());
        } finally {
            if (zos != null) {
                try {
                    zos.close();
                } catch (IOException e) {
                    log.error("打包文件时出错:", e);
                }
            }
        }
        return fileName;
    }

    /**
     * 设置页脚的参数
     * @param wb
     * @param footer
     */
    private void setFooter(Workbook wb, String[] footer) {

        //获取sheet的个数
        int zz = wb.getWorksheets().getCount();

        //设置企业内部编号的值
        String ret = "";
        for (int i = 0; i < footer.length; i++) {
            String footerRet = footer[i];
            if(footerRet.contains("&")){
                footerRet = footerRet.replace("&","&&");
            }
            //ret += footer[i];
            ret += footerRet;
        }
        //将企业内部编号放入页脚
        for (int z = 0; z < zz; z++){
            String f1 = wb.getWorksheets().get(z).getPageSetup().getFooter(1);
            wb.getWorksheets().get(z).getPageSetup().setFooter(1, String.format(f1+"  %s",ret));
        }
    }

    /**
     * 获取license
     *
     * @return
     */
    public static boolean getLicense() {
        boolean result = false;
        try {
            InputStream is = TestService.class.getClassLoader().getResourceAsStream("/files/license.xml");
            License aposeLic = new License();
            aposeLic.setLicense(is);
            result = true;
        } catch (Exception e) {
            e.printStackTrace();
        }
        return result;
    }


    public byte[] getExport(List<People> peopleList, List<KeyValuePair<String, String>> header, String excelName) throws Exception{
        // 获取要导出的数据源对象
        List<ExportConfig> configList = new ArrayList();
        for(KeyValuePair<String, String> kvp : header) {
            configList.add(new ExportConfig(kvp.getKey(), kvp.getValue()));
        }
        return ExportUtil.export(peopleList, configList, "xlsx", excelName);
    }

    /**
     * dayin
     * @param billType
     * @param printType
     * @param billListNos
     * @return
     * @throws Exception
     */
    public Map<String, String> printBill(String billType, String printType, List<String> billListNos) throws Exception {
        String templateName = "";
        if ("I".equals(billType)) {
            templateName = "iBillTemplate.xlsx";
        } else if ("E".equals(billType)) {
            templateName = "eBillTemplate.xlsx";
        }

        List<String> files = new ArrayList<>();
        Map<String, String> fileNameMap = new HashMap<>();
        for (String billListNo : billListNos) {
            String fileName = UUID.randomUUID().toString() + ".pdf";
            Map<String, Object> param = new HashMap<>(0);
            param.put("BILL_LIST_NO", billListNo);
            Triplet<List<DcBillHeadDto>, List<DcBillListDto>, List<DcEntryListDto>> expData = getExportData(param);
            String exportFileName = "";
            if (expData.getValue0().size() > 0) {
                exportFileName = new ExportUtil().exportExcel(expData.getValue0(), expData.getValue1(), expData.getValue2(), fileName, templateName, expData.getValue0().get(0).getEmsListNo());
                files.add(exportFileName);
                fileNameMap.put(exportFileName, expData.getValue0().get(0).getEmsListNo()+"-清单");
            }
        }

        Map<String, String> mapFile = new HashMap<>(2);
        String returnFile = "";
        String returnFileName = "";
        if("M".equals(printType)){
            if(files.size() > 1){
                returnFile = combinPdf(files, UUID.randomUUID().toString() + ".pdf");
                returnFileName = "核注清单打印.pdf";
            } else if(files.size()==1){
                returnFile = files.get(0);
                returnFileName = fileNameMap.get(files.get(0));
            } else{
                throw new ErrorException(400,"未找到待打印的核注清单");
            }
        } else{
            returnFile = this.toZip(files, fileNameMap);
            returnFileName = "核注清单打印.zip";
        }
        mapFile.put("returnFile", returnFile);
        mapFile.put("returnFileName", returnFileName);
        return mapFile;
    }

    /**
     * 获取需打印数据
     *
     * @param param
     * @return
     */
    public Triplet<List<DcBillHeadDto>, List<DcBillListDto>, List<DcEntryListDto>> getExportData(Map<String, Object> param) {
        List<DcBillHeadDto> billHeadDto = new ArrayList<>();
        List<DcBillListDto> billListDto = new ArrayList<>();
        List<DcEntryListDto> entryListDto = new ArrayList<>();
        return new Triplet<>(billHeadDto, billListDto, entryListDto);
    }

    /**
     * 获取报关单草稿数据
     * @param billListNo
     * @return
     */
    public Pair<List<DcBillHeadDto>, List<DcBillListDto>> getEntryDraftData(String billListNo) {
        List<DcBillHeadDto> val0 = new ArrayList<>();
        List<DcBillListDto> val1 = new ArrayList<>();
        return new Pair<>(val0, val1);
    }

    public String replaceNull(String str) {
        if (str == null) {
            str = "";
        }
        return str;
    }

    /**
     * 核注清单下载
     * @param billType
     * @param billListNos
     * @return
     * @throws Exception
     */
    public String downloadBill(String billType,  List<String> billListNos) throws Exception {
        String templateName="";
        String entryTemplateName="";
        if("I".equals(billType)) {
            templateName = "iBillTemDownLoad.xlsx";
            entryTemplateName = "iEntryDraft.xls";
        }else if("E".equals(billType)){
            templateName = "eBillTemDownLoad.xlsx";
            entryTemplateName = "eEntryDraft.xls";
        }
        String fileName = "";
        String entryFileName = "";
        String exportFileName = "";
        for (String billListNo : billListNos) {
            fileName = UUID.randomUUID().toString() + ".xlsx";
            entryFileName = UUID.randomUUID().toString() + ".xlsx";
            Map<String, Object> param = new HashMap<>(0);
            param.put("BILL_LIST_NO", billListNo);
            Triplet<List<DcBillHeadDto>, List<DcBillListDto>, List<DcEntryListDto>> expData = getExportData(param);
            Pair<List<DcBillHeadDto>, List<DcBillListDto>>  pairVal = getEntryDraftData(billListNo);
            //清单
            if (expData.getValue0().size() > 0) {
                new ExportUtil().exportExcel(expData.getValue0(), expData.getValue1(), expData.getValue2(), fileName, templateName);
            }
            //报关单草单
            if (pairVal.getValue0() != null && pairVal.getValue0().size() > 0) {
                pairVal.getValue0().get(0).setPageNum(selectPageCount(pairVal));
                new ExportUtil().exportExcel(pairVal.getValue0(), pairVal.getValue1(), null, entryFileName, entryTemplateName);
            }
            //合并
            exportFileName = this.combinExcel(fileName, entryFileName,"报关单草单");
        }
        return tempPath + exportFileName;
    }

    /**
     * 查询模板中 页码/页数: 字段的值
     * 第一页中表头+3条表体为一页，大于3条表体为两页
     * 第二页中9条表体+其他数据为一整页，整十条表体也为一整页
     * @param pairVal
     * @return
     */
    private String selectPageCount(Pair<List<DcBillHeadDto>, List<DcBillListDto>> pairVal) {
        int pc = 1;
        //如果list.size>3,那么减4之后对10取余
        if(pairVal.getValue1().size() > 3){
            int size = pairVal.getValue1().size()-4;
            //去除3条后是否大于等于10条
            int pageCount;
            if(size >= 10){
                pageCount = size/10;
                pc = pc + pageCount + 1;
            }else{
                //如果小于十条
                pc = pc + 1;
            }
        }
        return "1/"+pc;
    }

    /**
     * B.初始化分区(检测是否需要新建子分区)
     * @param hxCoreTask
     * @param calcMark
     */
    private void initNewBalancePartitions(HxPartitionParam hxCoreTask, String calcMark) {
        // 获取需要创建新分区的表
        HxPartitionParam hxPartitionParam = new HxPartitionParam();
        hxPartitionParam.setCreateMark("A");
        List<HxPartition> hxPartitions = partitionMapper.getList(hxPartitionParam);
        // 创建子分区
        for (HxPartition hxPartition : hxPartitions) {
            // 检查是否存在主分区
            String preFix = hxPartition.getPartitionTableName().toLowerCase().replace("t_hx_", "");
            String pgMasterTableName = preFix + "_" + hxCoreTask.getCavMark().toLowerCase();
            String oracleMasterTableName = hxPartition.getPartitionTableName().toUpperCase();
            Integer existMaster = partitionMapper.selectMasterPartitionExist(pgMasterTableName, oracleMasterTableName, hxCoreTask.getCavMark());
            int mainCount = existMaster == null ? 0 : existMaster;
            String mainPrefix = hxPartition.getPartitionTableName().toLowerCase().replace("t_hx_", "") + "_";
            String subPrefix = mainPrefix + hxCoreTask.getCavMark().toLowerCase() + "_";
            partitionMapper.createCalcSubPartition(hxPartition.getPartitionTableName(), hxCoreTask.getCavMark().toLowerCase(),
                    hxCoreTask.getCavMark().toUpperCase(), calcMark, mainPrefix, subPrefix, "calc_mark", mainCount);
        }
    }


    /**
     * 测试分区
     * @param mainValue:主分区值
     * @param subValue:子分区
     * @param subColumn:子分区列挂在主分区
     */
    public void createPartitionsIfNotExists(final String mainValue, final String subValue, final String subColumn) {
        StringFilterUtils.sqlFilter(mainValue);
        StringFilterUtils.sqlFilter(subValue);
        //final String subColumn = "arc_mark";
        Arrays.asList("T_HX_PRICE_AVG,PR_AVG", "T_HX_PRICE_AVG_TRACK,PR_TRK", "T_HX_PRICE_BILL_SOURCE,PR_SRC")
                .stream().forEach(t -> {
                    int mainCount;
                    int subCount;
                    final String tableName = t.split(",")[0];
                    final String mainPrefix = t.split(",")[1] + "_p_";
                    final String subPrefix = t.split(",")[1] + "_s_";
                    mainCount = partitionMapper.selectMainCount(tableName, mainValue, mainPrefix);
                    subCount = partitionMapper.selectSubCount(tableName, mainValue, subValue, mainPrefix, subPrefix);
                    if (subCount == 0) {
                        partitionMapper.createSubPartition(tableName, mainValue, subValue, mainPrefix, subPrefix, subColumn, mainCount);
                    }
                }
        );
    }

}
